/**
判断和统计Layer内某种颜色的像素

可以在遇到指定颜色的像素之后立即返回，也可以统计完包含某个颜色的像素的数量后返回

可以指定一个Layer作为mask，这时候会先把目标Layer的像素颜色值减去mask的对应位置的像素颜色值再进行统计

虽然用TJS也能实现这个功能，但是为了提高效率，还是写成dll比较好

注意，因为mask值做的是减法，浅色/alpha值大的部分会被掩掉，也就是说，mask中的不透明的白色会掩掉目标Layer中的任何颜色

这里的“减法”并不是颜色值的32位整数直接相减，而是ARGB的每个分量分别相减，且不会有负数，0x05100000减0x00800000的结果是0x05000000，而不是0x04900000

通常我们制作mask的时候都不会做成透明的，而这时候alpha部分做减法就会得0，所以这种情况下要检测的颜色值的A分量就要写00了

格式为 checkPixelColor(layer=要检测的目标Layer, color=要检测的颜色值0xAARRGGBB, [mask=作为mask的Layer 默认为void], [needCount=是否需要统计象素数量 默认为false])

返回值为监测到的指定颜色的像素的数量，如果遇到指定颜色的像素之后立即返回(默认就是这种情况)，则只会返回0或者1，用来判断Layer中是否包含指定的颜色的像素

*/

//---------------------------------------------------------------------------
#include <windows.h>
#include "tp_stub.h"
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// 判断和统计Layer内某种颜色的像素
//---------------------------------------------------------------------------
class tTJSCheckPixelColorFunction : public tTJSDispatch
{
	tjs_error TJS_INTF_METHOD FuncCall(
		tjs_uint32 flag, const tjs_char * membername, tjs_uint32 *hint,
		tTJSVariant *result,
		tjs_int numparams, tTJSVariant **param, iTJSDispatch2 *objthis);
} * TJSCheckPixelColorFunction;

tjs_error TJS_INTF_METHOD tTJSCheckPixelColorFunction::FuncCall(
	tjs_uint32 flag, const tjs_char * membername, tjs_uint32 *hint,
	tTJSVariant *result,
	tjs_int numparams, tTJSVariant **param, iTJSDispatch2 *objthis)
{
	if(numparams < 2) return TJS_E_BADPARAMCOUNT;

    const tjs_uint8 * bufferptr;
    tjs_int bufferpitch;
    tjs_int width;
    tjs_int height;

    // 画像のサイズ、ピクセルバッファへのポインタ、ピッチを得る
    iTJSDispatch2 * layerobj = param[0]->AsObjectNoAddRef();

    tTJSVariant val;
    if(TJS_FAILED(layerobj->PropGet(0, TJS_W("imageWidth"), NULL, &val, layerobj)))
        TVPThrowExceptionMessage(TJS_W("invoking of Layer.imageWidth failed."));
    width = val;

    if(TJS_FAILED(layerobj->PropGet(0, TJS_W("imageHeight"), NULL, &val, layerobj)))
        TVPThrowExceptionMessage(TJS_W("invoking of Layer.imageHeight failed."));
    height = val;

    if(TJS_FAILED(layerobj->PropGet(0, TJS_W("mainImageBuffer"), NULL, &val, layerobj)))
        TVPThrowExceptionMessage(TJS_W("invoking of Layer.mainImageBuffer failed."));
    bufferptr = (const tjs_uint8 *)(tjs_int)val;

    if(TJS_FAILED(layerobj->PropGet(0, TJS_W("mainImageBufferPitch"), NULL, &val, layerobj)))
        TVPThrowExceptionMessage(TJS_W("invoking of Layer.mainImageBufferPitch failed."));
    bufferpitch = val;

    // 要检测的颜色值
    tjs_uint32 color = param[1]->AsInteger();

    // mask图层的图像缓冲区数据
    const tjs_uint8 * maskbufferptr = NULL;
    tjs_int maskbufferpitch;
    tjs_int maskwidth;
    tjs_int maskheight;

    if(numparams >= 3 && param[2]->Type() == tvtObject)
    {
        layerobj = param[2]->AsObjectNoAddRef();

        if(layerobj->IsInstanceOf(TJS_MEMBERMUSTEXIST, NULL, NULL, TJS_W("Layer"), NULL) == TJS_S_TRUE)
        {
            if(TJS_FAILED(layerobj->PropGet(0, TJS_W("imageWidth"), NULL, &val, layerobj)))
                TVPThrowExceptionMessage(TJS_W("invoking of Layer.imageWidth failed."));
            maskwidth = val;

            if(TJS_FAILED(layerobj->PropGet(0, TJS_W("imageHeight"), NULL, &val, layerobj)))
                TVPThrowExceptionMessage(TJS_W("invoking of Layer.imageHeight failed."));
            maskheight = val;

            if(TJS_FAILED(layerobj->PropGet(0, TJS_W("mainImageBuffer"), NULL, &val, layerobj)))
                TVPThrowExceptionMessage(TJS_W("invoking of Layer.mainImageBuffer failed."));
            maskbufferptr = (const tjs_uint8 *)(tjs_int)val;

            if(TJS_FAILED(layerobj->PropGet(0, TJS_W("mainImageBufferPitch"), NULL, &val, layerobj)))
                TVPThrowExceptionMessage(TJS_W("invoking of Layer.mainImageBufferPitch failed."));
            maskbufferpitch = val;
        }
    }

    // 是否需要统计指定颜色的像素数量 默认为否
    bool needCount = false;

    if(numparams >= 4)
        needCount = (bool)param[3];

    tTVInteger pixelcount = 0;
    tjs_uint32 colorsrc, colormask;
    if(maskbufferptr == NULL) // 没有mask
    {
        for(int i=0; i<height; i++)
        {
            for(int j=0; j<width; j++)
            {
                colorsrc = *(((tjs_uint32*)(bufferptr + i * bufferpitch)) + j);
                if(colorsrc == color)
                {
                    pixelcount++;
                    if(!needCount)
                    {
                        if(result)
                        {
                            *result = pixelcount;
                        }
                        return TJS_S_OK;
                    }
                }
            }
        }
    }
    else // 有mask
    {
        for(int i=0; i<height; i++)
        {
            for(int j=0; j<width; j++)
            {
                colorsrc = *(((tjs_uint32*)(bufferptr + i * bufferpitch)) + j);
                colormask = *(((tjs_uint32*)(maskbufferptr + i * maskbufferpitch)) + j);
                colorsrc = ((colorsrc & 0xFF000000) > (colormask & 0xFF000000) ? (colorsrc & 0xFF000000) - (colormask & 0xFF000000) : 0)
                         | ((colorsrc & 0x00FF0000) > (colormask & 0x00FF0000) ? (colorsrc & 0x00FF0000) - (colormask & 0x00FF0000) : 0)
                         | ((colorsrc & 0x0000FF00) > (colormask & 0x0000FF00) ? (colorsrc & 0x0000FF00) - (colormask & 0x0000FF00) : 0)
                         | ((colorsrc & 0x000000FF) > (colormask & 0x000000FF) ? (colorsrc & 0x000000FF) - (colormask & 0x000000FF) : 0);
                if(colorsrc == color)
                {
                    pixelcount++;
                    if(!needCount)
                    {
                        if(result)
                        {
                            *result = pixelcount;
                        }
                        return TJS_S_OK;
                    }
                }
            }
        }
    }

	if(result)
	{
		*result = pixelcount;
	}

	return TJS_S_OK;
}
//---------------------------------------------------------------------------





//---------------------------------------------------------------------------
#pragma argsused
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason,
	void* lpReserved)
{
	return 1;
}
//---------------------------------------------------------------------------
static tjs_int GlobalRefCountAtInit = 0;
extern "C" HRESULT _stdcall _export V2Link(iTVPFunctionExporter *exporter)
{
	// スタブの初期化(必ず記述する)
	TVPInitImportStub(exporter);

	// AverageFunction の作成と登録
	tTJSVariant val;

	// TJS のグローバルオブジェクトを取得する
	iTJSDispatch2 * global = TVPGetScriptDispatch();



	//-----------------------------------------------------------------------
	// 1 まずオブジェクトを作成
	TJSCheckPixelColorFunction = new tTJSCheckPixelColorFunction();

	// 2 AverageFunction を tTJSVariant 型に変換
	val = tTJSVariant(TJSCheckPixelColorFunction);

	// 3 すでに val が AverageFunction を保持しているので、AverageFunction は
	//   Release する
	TJSCheckPixelColorFunction->Release();


	// 4 global の PropSet メソッドを用い、オブジェクトを登録する
	global->PropSet(
		TJS_MEMBERENSURE, // メンバがなかった場合には作成するようにするフラグ
		TJS_W("checkPixelColor"), // メンバ名 ( かならず TJS_W( ) で囲む )
		NULL, // ヒント ( 本来はメンバ名のハッシュ値だが、NULL でもよい )
		&val, // 登録する値
		global // コンテキスト ( global でよい )
		);
	//-----------------------------------------------------------------------

	// - global を Release する
	global->Release();

	// もし、登録する関数が複数ある場合は 1 ～ 4 を繰り返す


	// val をクリアする。
	// これは必ず行う。そうしないと val が保持しているオブジェクト
	// が Release されず、次に使う TVPPluginGlobalRefCount が正確にならない。
	val.Clear();


	// この時点での TVPPluginGlobalRefCount の値を
	GlobalRefCountAtInit = TVPPluginGlobalRefCount;
	// として控えておく。TVPPluginGlobalRefCount はこのプラグイン内で
	// 管理されている tTJSDispatch 派生オブジェクトの参照カウンタの総計で、
	// 解放時にはこれと同じか、これよりも少なくなってないとならない。
	// そうなってなければ、どこか別のところで関数などが参照されていて、
	// プラグインは解放できないと言うことになる。

	return S_OK;
}
//---------------------------------------------------------------------------
extern "C" HRESULT _stdcall _export V2Unlink()
{
	// 吉里吉里側から、プラグインを解放しようとするときに呼ばれる関数。

	// もし何らかの条件でプラグインを解放できない場合は
	// この時点で E_FAIL を返すようにする。
	// ここでは、TVPPluginGlobalRefCount が GlobalRefCountAtInit よりも
	// 大きくなっていれば失敗ということにする。
	if(TVPPluginGlobalRefCount > GlobalRefCountAtInit) return E_FAIL;
		// E_FAIL が帰ると、Plugins.unlink メソッドは偽を返す

	// TJS のグローバルオブジェクトに登録した average 関数などを削除する

	// - まず、TJS のグローバルオブジェクトを取得する
	iTJSDispatch2 * global = TVPGetScriptDispatch();

	// - global の DeleteMember メソッドを用い、オブジェクトを削除する
	if(global)
	{
		// TJS 自体が既に解放されていたときなどは
		// global は NULL になり得るので global が NULL でない
		// ことをチェックする

		global->DeleteMember(
			0, // フラグ ( 0 でよい )
			TJS_W("checkPixelColor"), // メンバ名
			NULL, // ヒント
			global // コンテキスト
			);
	}

	// - global を Release する
	if(global) global->Release();

	// スタブの使用終了(必ず記述する)
	TVPUninitImportStub();

	return S_OK;
}
//---------------------------------------------------------------------------
/*
	V2Link と V2Unlink は DLL からエクスポートされている必要がある。
	従って、.def ファイルを作成し

EXPORTS
	V2Link
	V2Unlink

	と記述してプロジェクトに追加する。

	GetOptionsDesc を使う場合はそれも追加する。
*/
//---------------------------------------------------------------------------

